home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / REPLACE.C < prev    next >
C/C++ Source or Header  |  1993-04-20  |  12KB  |  519 lines

  1. /*
  2.  * replace.c
  3.  *
  4.  * Functions, formerly in search.c, which handle the search and replace
  5.  * functionality.
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include "estruct.h"
  10. #include "eproto.h"
  11. #include "edef.h"
  12. #include "elang.h"
  13.  
  14. static int    replen;            /* length of replacement string */
  15.  
  16. /*
  17.  * sreplace -- Search and replace.
  18.  */
  19. int PASCAL NEAR sreplace(f, n)
  20. int f;        /* default flag */
  21. int n;        /* # of repetitions wanted */
  22. {
  23.     return (replaces(FALSE, f, n));
  24. }
  25.  
  26. /*
  27.  * qreplace -- search and replace with query.
  28.  */
  29. int PASCAL NEAR qreplace(f, n)
  30. int f;        /* default flag */
  31. int n;        /* # of repetitions wanted */
  32. {
  33.     return (replaces(TRUE, f, n));
  34. }
  35.  
  36. /*
  37.  * replaces -- Search for a string and replace it with another
  38.  *    string.  Query might be enabled (according to kind).
  39.  */
  40. int PASCAL NEAR    replaces(kind, f, n)
  41. int    kind;    /* Query enabled flag */
  42. int    f;    /* default flag */
  43. int    n;    /* # of repetitions wanted */
  44. {
  45.     register int status;    /* success flag on pattern inputs */
  46.     register int nummatch;    /* number of found matches */
  47.     int numsub;        /* number of substitutions */
  48.     int nlflag;        /* last char of search string a <NL>? */
  49.     int nlrepl;        /* was a replace done on the last line? */
  50.     char c;            /* input char for query */
  51.     LINE *origline;        /* original "." position */
  52.     int origoff;        /* and offset (for . query option) */
  53.     LINE *lastline;        /* position of last replace and */
  54.     int lastoff;        /* offset (for 'u' query option) */
  55.     int oldmatchlen;    /* Closure may alter the match length.*/
  56.     static char *oldpatmatch = NULL;    /* allocated memory for un-do.*/
  57.  
  58.     /*
  59.      * Don't allow this command if we are
  60.      * in read only mode.
  61.      */
  62.     if (curbp->b_mode & MDVIEW)
  63.         return(rdonly());
  64.  
  65.     /* Check for negative repetitions.
  66.      */
  67.     if (f && n < 0)
  68.         return(FALSE);
  69.  
  70.     /* Ask the user for the text of a pattern.
  71.      */
  72.     if ((status = readpattern(kind? TEXT85: TEXT84, &pat[0], TRUE)) != TRUE)
  73. /*                "Replace" */
  74. /*                        "Query replace" */
  75.         return(status);
  76.  
  77.     /* Ask for the replacement string, and get its length.
  78.      */
  79.     if ((status = readpattern(TEXT86, &rpat[0], FALSE)) == ABORT)
  80. /*                "with" */
  81.         return(status);
  82.  
  83.     /* Set up flags so we can make sure not to do a recursive
  84.      * replace on the last line.
  85.      */
  86.     nlflag = (pat[matchlen - 1] == '\r');
  87.     nlrepl = FALSE;
  88.  
  89.     /* Save original . position, reset the number of matches and
  90.      * substitutions, and scan through the file.
  91.      */
  92.     origline = curwp->w_dotp;
  93.     origoff = curwp->w_doto;
  94.     numsub = 0;
  95.     nummatch = 0;
  96.     lastline = (LINE *)NULL;
  97.     mmove_flag = FALSE;     /* disable mouse move events          */
  98.  
  99.     while ( (f == FALSE || n > nummatch) &&
  100.         (nlflag == FALSE || nlrepl == FALSE) ) {
  101.         /* Search for the pattern.
  102.          * If we search with a regular expression,
  103.          * matchlen is reset to the true length of
  104.          * the matched string.
  105.          */
  106. #if    MAGIC
  107.         if (magical && (curwp->w_bufp->b_mode & MDMAGIC)) {
  108.             if (!mcscanner(FORWARD, PTBEG, 1))
  109.                 break;
  110.         }
  111.         else
  112. #endif
  113.             if (!scanner(FORWARD, PTBEG, 1))
  114.                 break;        /* all done */
  115.  
  116.         ++nummatch;    /* Increment # of matches */
  117.  
  118.         /* Check if we are on the last line.
  119.          */
  120.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  121.  
  122.         /* Check for query.
  123.          */
  124.         if (kind) {
  125.             /* Get the query.
  126.              */
  127. pprompt:        mlrquery();
  128. qprompt:
  129.             /* Show the proposed place to change, and
  130.              * update the position on the modeline if needed.
  131.              */
  132.             if (posflag)
  133.                 upmode();
  134.             update(TRUE);
  135.             c = tgetc();            /* and input */
  136.             mlerase();            /* and clear it */
  137.  
  138.             /* And respond appropriately.
  139.              */
  140.             switch (c) {
  141. #if    FRENCH
  142.                 case 'o':    /* oui, substitute */
  143.                 case 'O':
  144. #endif
  145.                 case 'y':    /* yes, substitute */
  146.                 case 'Y':
  147.                 case ' ':
  148.                     break;
  149.  
  150.                 case 'n':    /* no, onward */
  151.                 case 'N':
  152.                     forwchar(FALSE, 1);
  153.                     continue;
  154.  
  155.                 case '!':    /* yes/stop asking */
  156.                     kind = FALSE;
  157.                     break;
  158.  
  159.                 case 'u':    /* undo last and re-prompt */
  160.                 case 'U':
  161.                     /* Restore old position.
  162.                      */
  163.                     if (lastline == (LINE *)NULL) {
  164.                         /* There is nothing to undo.
  165.                          */
  166.                         TTbeep();
  167.                         goto pprompt;
  168.                     }
  169.                     curwp->w_dotp = lastline;
  170.                     curwp->w_doto = lastoff;
  171.                     lastline = NULL;
  172.                     lastoff = 0;
  173.  
  174.                     /* Delete the new string,
  175.                      * restore the old match.
  176.                      */
  177.                     backchar(FALSE, replen);
  178.                     status = delins(replen, oldpatmatch, FALSE);
  179.                     if (status != TRUE) {
  180.                         mmove_flag = TRUE;
  181.                         return(status);
  182.                     }
  183.  
  184.                     /* Record one less substitution,
  185.                      * backup, save our place, and
  186.                      * reprompt.
  187.                      */
  188.                     --numsub;
  189.                     backchar(FALSE, oldmatchlen);
  190.                     matchlen = oldmatchlen;
  191.                     matchline = curwp->w_dotp;
  192.                     matchoff  = curwp->w_doto;
  193.                     continue;
  194.  
  195.                 case '.':    /* abort! and return */
  196.                     /* restore old position */
  197.                     curwp->w_dotp = origline;
  198.                     curwp->w_doto = origoff;
  199.                     curwp->w_flag |= WFMOVE;
  200.  
  201.                 case BELL:    /* abort! and stay */
  202.                     mlwrite(TEXT89);
  203. /*                        "Aborted!" */
  204.                     mmove_flag = TRUE;
  205.                     return(FALSE);
  206.  
  207.                 default:    /* bitch and beep */
  208.                     TTbeep();
  209.  
  210.                 case '?':    /* help me */
  211.                     mlwrite(TEXT90);
  212. /*"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "*/
  213.                     goto qprompt;
  214.  
  215.             }    /* end of switch */
  216.         }    /* end of "if kind" */
  217.  
  218.         /* if this is the point origin, flag so we a can reset it */
  219.         if (curwp->w_dotp == origline) {
  220.             origline = NULL;
  221.             lastline = lback(curwp->w_dotp);
  222.         }
  223.  
  224.         /* Delete the sucker, and insert its
  225.          * replacement.
  226.          */
  227. #if    MAGIC
  228.         status = delins(matchlen, &rpat[0], rmagical);
  229. #else
  230.         status = delins(matchlen, &rpat[0], FALSE);
  231. #endif
  232.         if (origline == NULL) {
  233.             origline = lforw(lastline);
  234.             origoff = 0;
  235.         }
  236.  
  237.         if (status != TRUE) {
  238.             mmove_flag = TRUE;
  239.             return(status);
  240.         }
  241.  
  242.         numsub++;    /* increment # of substitutions */
  243.  
  244.         /* Save our position, the match length, and the string
  245.          * matched if we are query-replacing, as we may undo
  246.          * the replacement. If we are not querying, check to
  247.          * make sure that we didn't replace an empty string
  248.          * (possible in MAGIC mode), because we'll infinite loop.
  249.          */
  250.         if (kind) {
  251.             lastline = curwp->w_dotp;
  252.             lastoff = curwp->w_doto;
  253.             oldmatchlen = matchlen;    /* Save the length for un-do.*/
  254.  
  255. #if    (TURBO || ZTC) && (DOS16M == 0)
  256.             /* For compilers with reallocs that handle
  257.              * NULL pointers.
  258.              */
  259.             if ((oldpatmatch = realloc(oldpatmatch, matchlen + 1)) == NULL) {
  260.                 mlabort(TEXT94);
  261. /*                    "%%Out of memory" */
  262.                 mmove_flag = TRUE;
  263.                 return(ABORT);
  264.             }
  265. #else
  266.             if (oldpatmatch != NULL)
  267.                 free(oldpatmatch);
  268.  
  269.             if ((oldpatmatch = malloc(matchlen + 1)) == NULL) {
  270.                 mlabort(TEXT94);
  271. /*                    "%%Out of memory" */
  272.                 mmove_flag = TRUE;
  273.                 return(ABORT);
  274.             }
  275. #endif
  276.             strcpy(oldpatmatch, patmatch);
  277.         }
  278.         else if (matchlen == 0) {
  279.             mlwrite(TEXT91);
  280. /*                "Empty string replaced, stopping." */
  281.             mmove_flag = TRUE;
  282.             return(FALSE);
  283.         }
  284.     }
  285.  
  286.     /* And report the results.
  287.      */
  288.     mlwrite(TEXT92, numsub);
  289. /*        "%d substitutions" */
  290.     mmove_flag = TRUE;
  291.     return(TRUE);
  292. }
  293.  
  294. /*
  295.  * mlrquery -- The prompt for query-replace-string.
  296.  */
  297. VOID PASCAL NEAR mlrquery()
  298. {
  299.     register int    tcol;
  300. #if    MAGIC
  301.     register RMC    *rmcptr;
  302. #endif
  303.  
  304.     mlwrite(TEXT87);
  305. /*        "Replace '" */
  306.  
  307.     tcol = echostring(patmatch, strlen(TEXT87), NPAT/2);
  308.  
  309.     mlputs(TEXT88);
  310. /*        "' with '" */
  311.     tcol += strlen(TEXT88);
  312.  
  313. #if    MAGIC
  314.     if (rmagical && (curwp->w_bufp->b_mode & MDMAGIC)) {
  315.         rmcptr = &rmcpat[0];
  316.  
  317.         while (rmcptr->mc_type != MCNIL && tcol < NPAT - 8) {
  318.             if (rmcptr->mc_type == LITSTRING)
  319.                 tcol = echostring(rmcptr->u.rstr, tcol, NPAT - 8);
  320.             else if (rmcptr->mc_type == DITTO)
  321.                 tcol = echostring(patmatch, tcol, NPAT - 8);
  322.             else
  323.                 tcol = echostring(grpmatch[rmcptr->u.group_no],
  324.                     tcol, NPAT - 8);
  325.             rmcptr++;
  326.         }
  327.     }
  328.     else
  329. #endif
  330.         echostring(rpat, tcol, NPAT - 8);
  331.  
  332.     mlputs("'? ");
  333. }
  334.  
  335. /*
  336.  * delins -- Delete a specified length from the current point
  337.  *    then either insert the string directly, or make use of
  338.  *    replacement meta-array.
  339.  */
  340. int PASCAL NEAR delins(dlength, instr, use_rmc)
  341. int    dlength;
  342. char    *instr;
  343. int    use_rmc;
  344. {
  345.     register int    status;
  346.     register char    *rstr;
  347. #if    MAGIC
  348.     register RMC    *rmcptr;
  349. #endif
  350.  
  351.     replen = 0;
  352.  
  353.     /* Zap what we gotta,
  354.      * and insert its replacement.
  355.      */
  356.     if ((status = ldelete((long) dlength, FALSE)) != TRUE)
  357.         mlwrite(TEXT93);
  358. /*            "%%ERROR while deleting" */
  359.     else
  360. #if    MAGIC
  361.         if (use_rmc && (curwp->w_bufp->b_mode & MDMAGIC)) {
  362.             rmcptr = &rmcpat[0];
  363.             while (rmcptr->mc_type != MCNIL && status == TRUE) {
  364.                 if (rmcptr->mc_type == LITSTRING)
  365.                     status = linstr(rstr = rmcptr->u.rstr);
  366.                 else if (rmcptr->mc_type == DITTO)
  367.                     status = linstr(rstr = patmatch);
  368.                 else
  369.                     status = linstr(rstr = fixnull(grpmatch[rmcptr->u.group_no]));
  370.                 replen += strlen(rstr);
  371.                 rmcptr++;
  372.             }
  373.         }
  374.         else
  375. #endif
  376.         {
  377.             status = linstr(instr);
  378.             replen = strlen(instr);
  379.         }
  380.  
  381.     return (status);
  382. }
  383.  
  384. /*
  385.  * rmcstr -- Set up the replacement 'magic' array.  Note that if there
  386.  *    are no meta-characters encountered in the replacement string,
  387.  *    the array is never actually created - we will just use the
  388.  *    character array rpat[] as the replacement string.
  389.  */
  390. int PASCAL NEAR rmcstr()
  391. {
  392.     RMC    *rmcptr;
  393.     char    *patptr;
  394.     int    pchr;
  395.     int    status = TRUE;
  396.     int    mj;
  397.  
  398.     patptr = (char *)&rpat[0];
  399.     rmcptr = &rmcpat[0];
  400.     mj = 0;
  401.     rmagical = FALSE;
  402.  
  403.     while (*patptr && status == TRUE) {
  404.         switch (*patptr) {
  405.             case MC_DITTO:
  406.  
  407.                 /* If there were non-magical characters in
  408.                  * the string before reaching this character
  409.                  * plunk it in the replacement array before
  410.                  * processing the current meta-character.
  411.                  */
  412.                 if (mj != 0) {
  413.                     rmcptr->mc_type = LITSTRING;
  414.                     if ((rmcptr->u.rstr = malloc(mj + 1)) == NULL) {
  415.                         mlabort(TEXT94);
  416. /*                            "%%Out of memory" */
  417.                         status = FALSE;
  418.                         break;
  419.                     }
  420.                     bytecopy(rmcptr->u.rstr, patptr - mj, mj);
  421.                     rmcptr++;
  422.                     mj = 0;
  423.                 }
  424.                 rmcptr->mc_type = DITTO;
  425.                 rmcptr++;
  426.                 rmagical = TRUE;
  427.                 break;
  428.  
  429.             case MC_ESC:
  430.                 pchr = *(patptr + 1);    /* peek at next char.*/
  431.                 if (pchr <= '9' && pchr >= '1') {
  432.                     if (mj != 0) {
  433.                         rmcptr->mc_type = LITSTRING;
  434.                         if ((rmcptr->u.rstr = malloc(mj + 1)) == NULL) {
  435.                             mlabort(TEXT94);
  436. /*                            "%%Out of memory" */
  437.                             status = FALSE;
  438.                             break;
  439.                         }
  440.                         bytecopy(rmcptr->u.rstr, patptr - mj, mj);
  441.                         rmcptr++;
  442.                         mj = 0;
  443.                     }
  444.                     rmcptr->mc_type = GROUP;
  445.                     rmcptr->u.group_no = pchr - '0';
  446.                     patptr++;
  447.                 }
  448.                 else
  449.                 {
  450.                     rmcptr->mc_type = LITSTRING;
  451.  
  452.                     /* We malloc mj plus two here, instead
  453.                      * of one, because we have to count the
  454.                      * current character.
  455.                      */
  456.                     if ((rmcptr->u.rstr = malloc(mj + 2)) == NULL) {
  457.                         mlabort(TEXT94);
  458. /*                        "%%Out of memory" */
  459.                         status = FALSE;
  460.                         break;
  461.                     }
  462.  
  463.                     bytecopy(rmcptr->u.rstr, patptr - mj, mj + 1);
  464.  
  465.                     /* If MC_ESC is not the last character
  466.                      * in the string, find out what it is
  467.                      * escaping, and overwrite the last
  468.                      * character with it.
  469.                      */
  470.                     if (pchr != '\0') {
  471.                         *((rmcptr->u.rstr) + mj) = pchr;
  472.                         patptr++;
  473.                     }
  474.                     mj = 0;
  475.                 }
  476.  
  477.                 rmcptr++;
  478.                 rmagical = TRUE;
  479.                 break;
  480.  
  481.             default:
  482.                 mj++;
  483.         }
  484.         patptr++;
  485.     }
  486.  
  487.     if (rmagical && mj > 0) {
  488.         rmcptr->mc_type = LITSTRING;
  489.         if ((rmcptr->u.rstr = malloc(mj + 1)) == NULL) {
  490.             mlabort(TEXT94);
  491. /*                "%%Out of memory" */
  492.             status = FALSE;
  493.         }
  494.         bytecopy(rmcptr->u.rstr, patptr - mj, mj);
  495.         rmcptr++;
  496.     }
  497.  
  498.     rmcptr->mc_type = MCNIL;
  499.     return (status);
  500. }
  501.  
  502. /*
  503.  * rmcclear -- Free up any strings, and MCNIL the RMC array.
  504.  */
  505. VOID PASCAL NEAR rmcclear()
  506. {
  507.     register RMC    *rmcptr;
  508.  
  509.     rmcptr = &rmcpat[0];
  510.  
  511.     while (rmcptr->mc_type != MCNIL) {
  512.         if (rmcptr->mc_type == LITSTRING)
  513.             free(rmcptr->u.rstr);
  514.         rmcptr++;
  515.     }
  516.  
  517.     rmcpat[0].mc_type = MCNIL;
  518. }
  519.